//controller 暴露出去的是一个类
const path = require('path');
const fse = require('fs-extra');
const multiparty = require('multiparty');

const UPLOAD_DIR = path.resolve(__dirname, 'target');//指的是当前目录，当前目录下的target
const UPLOADED = path.resolve(UPLOAD_DIR, '..', 'uploaded');


module.exports = class {

    async handleVerifyUpload(req, res) {
        console.log('handleVerifyUpload-------------start');
        // 服务器端有没有这个文件
        // 拿到post 的 data, bodyParser
        const { filename, fileHash } = await resolvePost(req);
        // console.log(filename,fileHash);
        const ext = extractExt(filename);
        // console.log(ext);
        // const filePath = path.resolve(UPLOAD_DIR, `${fileHash}${ext}`);

        const uploadedDir = path.resolve(UPLOADED);
        if (!fse.existsSync(uploadedDir)) {
            await fse.mkdir(uploadedDir);
        }

        const filePath = path.resolve(UPLOADED, `${fileHash}${ext}`);
        // console.log(filePath);
        if (fse.existsSync(filePath)) { // 整个文件已经存在了，即已经合并了
            res.end(
                JSON.stringify({
                    shouldUpload: false
                })
            );
        } else {
            res.end(
                JSON.stringify({
                    shouldUpload: true,
                    uploadedList: await createUploadedList(fileHash)
                })
            );
        }
        console.log('handleVerifyUpload-------------end');

    }


    async handleFormData(req, res) {
        console.log('handleFormData-------------start');
        // multiparty.Form(); 用来解析带有文件上传的表单
        /*  
            在 multiparty.parse 的回调中，(err, fields, files)
            files 参数保存了 FormData 中文件，
            fields 参数保存了 FormData 中非文件的字段
        */
        const multipart = new multiparty.Form();
        multipart.parse(req, async (err, fields, files) => {
            if (err) {
                console.error(err);
                res.status = 500;
                res.end('process file chunk failed');
                return;
            }

            // console.log('err,fields,files', err, fields, files);
            //chunk.path //文件传过来时会先存放在服务器的某个临时目录下，等下我们要将他移动到特定目录下
            const [chunk] = files.chunk;
            const [hash] = fields.hash;//hash-index
            const [filename] = fields.filename;// 如 yb.jpeg
            const [fileHash] = fields.fileHash; //hash

            const ext = extractExt(filename);// .jpeg
            //filePath 合成后的文件路径
            const filePath = path.resolve(UPLOADED, `${fileHash}${ext}`);
            // console.log('filePath',filePath);

            if (fse.existsSync(filePath)) { //查看文件是否已经上传,是否已经存在
                console.log('文件已经上传~（已合并）');
                res.end('file exist')
                return;
            }

            // 以文件hash名作为合并时的临时目录
            const chunkDir = path.resolve(UPLOAD_DIR, fileHash);

            // 切片目录不存在，创建切片目录
            if (!fse.existsSync(chunkDir)) {
                await fse.mkdirs(chunkDir);
            }
            //将chunk移动到该临时目录中
            await fse.moveSync(chunk.path, path.resolve(chunkDir, hash));
            console.log('handleFormData-------------end');
            res.end("received file chunk")
        })
    }

    async handleMerge(req, res) {
        console.log('handleMerge-------------start');
        /* merge post请求发送来的json数据
          size:SIZE,
          fileHash:this.container.hash, // 文件的hash
          filename: this.container.file.name
        */
        const { size, filename, fileHash } = await resolvePost(req);

        // console.log( 'size,filename, fileHash',size,filename, fileHash);
        // 524288 yb.jpeg 24b42dbd786fbd1bcdac21be9c77de70
        const ext = extractExt(filename)

        const filePath = path.resolve(UPLOADED, `${fileHash}${ext}`);
        console.log('filePath', filePath);
        await mergeFileChunk(filePath, fileHash, size);
        console.log('handleMerge-------------end');
        res.end(
            JSON.stringify({
                code: 0,
                message: " handleMerge file merged success"
            })
        )
        // res.end(xxx)不能写多，写多了会报错
        // res.end('handleMerge finish end')

    }
}

// 返回已经上传切片名列表
const createUploadedList = async fileHash =>{
    let chunkDir =  path.resolve(UPLOAD_DIR,fileHash);
    return fse.existsSync(chunkDir)? await fse.readdir(chunkDir):[];
}

const mergeFileChunk = async (filePath, fileHash, size) => {
    // console.log('mergeFileChunk', filePath, fileHash, size);
    const chunkDir = path.resolve(UPLOAD_DIR, fileHash);
    const chunks = await fse.readdir(chunkDir);//读出文件夹中所有文件
    //对文件排序
    chunks.sort((a, b) => a.split('-')[1] - b.split('-')[1]);
    console.log(chunks);

    //每块chunks分别写到合并后文件对应的地方
    await Promise.all(
        chunks.map((chunk, index) =>
            pipeStream(
                path.resolve(chunkDir, chunk),
                fse.createWriteStream(filePath, {// 创建写入流，（最终合并后文件，指定每块存的地方）
                    start: index * size,
                    end: (index + 1) * size
                })
            )
        )
    ).then(() => {
        console.log('mergeFileChunk ===> 合并文件完成');
        //删除临时文件夹
        fse.rmdirSync(chunkDir)
        console.log('mergeFileChunk ===> 成功删除临时文件夹', chunkDir);
    })




}

/**
 * 
 * @param {*} path 每个分块文件的路径
 * @param {*} writeStream 写流
 */
const pipeStream = (path, writeStream) => {
    return new Promise(resolve => {
        const readStream = fse.createReadStream(path);
        readStream.on('end', () => {
            //读完之后,删除块文件
            fse.unlink(path);
            console.log('pipeStream ===> 成功删除块文件', path);
            resolve('读写流(合并)写入完成');
        })
        readStream.pipe(writeStream);
    })
}


/**
 * 取出文件名中的扩展名
 * @param {*} filename 
 */
const extractExt = (filename) => {
    //str.slice(start,end) //数组也行
    return filename.slice(filename.lastIndexOf("."));
}

// post ==> 事件接收!
// req.on('data',(data)=>{}) : 每次发送的数据
// req.on('end',()=>{})  : 数据发送完成
const resolvePost = (req) => {

    return new Promise(resolve => {
        // post 是慢慢的来的(一些一些来的，所以要拼接)
        let chunk = "";
        req.on("data", data => {
            chunk += data;  //二进制
        })
        req.on("end", () => {
            console.log(`post请求${req.url}传来的json数据`, chunk);
            resolve(JSON.parse(chunk))
        })
    })
}
